# -*- coding:utf-8 -*-
# time: 2025/7/16 ♡  18:21
# author:张伟
import os
import random
import time
from hashlib import md5
from io import BytesIO

from flask import Blueprint, render_template, redirect, url_for, request, jsonify, make_response, session, g, \
    current_app
from flask_avatars import Identicon
from flask_jwt_extended import create_access_token, create_refresh_token, get_jwt_identity, jwt_required
from flask_paginate import Pagination, get_page_parameter
from sqlalchemy import func
from sqlalchemy.exc import DatabaseError

import bbs_celery
import config
from apps.front.decorators import login_required
from apps.front.forms import RegisterForm, LoginForm, UploadAvatarForm, SignatureForm, UploadImageForm, PostPublicForm, \
    CommentForm, LikeForm, CheckEmailCaptchaForm, VarifyNewEmailForm, SetCountForm, PostEditForm
from exts import cache, db
from models.auth import UserModel, Permission
from models.post import BoardModel, PostModel, CommentModel, LikeModel, BannerModel
from utils import restful
from utils.captcha import Captcha

ft_bp = Blueprint('front', __name__, url_prefix='/')


@ft_bp.before_request
def before_request():
    if "user_id" in session:
        user_id = session.get("user_id")
        user = UserModel.query.get(user_id)
        if user:
            if not user.is_active:
                session.pop("user_id", None)
                return redirect("/login/")
            else:
                setattr(g, "user", user)
        else:
            session.pop("user_id", None)


@ft_bp.context_processor
def front_context_processor():
    if hasattr(g, "user"):
        return {"user": g.user}
    else:
        return {"user": None}



@ft_bp.route('/')
def index():
    sort = request.args.get("st", default=1, type=int)
    board_id = request.args.get("bd", type=int, default=-1)
    page = request.args.get(get_page_parameter(), type=int, default=1)
    start = (page - 1) * config.PAGE_SIZE
    end = start + config.PAGE_SIZE

    post_query = None
    total = 0
    # 使用LEFT JOIN确保所有帖子都被包含
    comment_count_result = db.session.query(PostModel.id.label('post_id'),
                                            func.coalesce(func.count(CommentModel.id), 0).label('comment_num')
                                            ).outerjoin(
        CommentModel,
        PostModel.id == CommentModel.post_id
    ).group_by(PostModel.id).all()

    comment_count = {item.post_id: item.comment_num for item in comment_count_result}

    post_query = PostModel.query
    if board_id != -1:
        post_query = post_query.filter_by(board_id=board_id)
    # 按发布时间排序
    if sort == 1:
        post_query = PostModel.query.order_by(PostModel.create_time.desc())
    # 按照点赞数排序
    elif sort == 2:
        post_query = PostModel.query.order_by(PostModel.likes_num.desc(), PostModel.create_time.desc())
    # 按照评论数排序
    elif sort == 3:
        post_query = db.session.query(PostModel).outerjoin(CommentModel, PostModel.id == CommentModel.post_id).group_by(
            PostModel.id).order_by(func.count(CommentModel.id).desc(), PostModel.create_time.desc())
    # 其他情况按照发布时间排序
    else:
        post_query = PostModel.query.order_by(PostModel.create_time.desc())

    posts = post_query.slice(start, end)
    total = post_query.count()

    pagination = Pagination(bs_version=3, page=page, total=total, per_page=config.PAGE_SIZE, outer_window=0,
                            inner_window=2)

    context = {
        "boards": BoardModel.query.order_by(BoardModel.priority.desc()).all(),
        "posts": posts,
        "pagination": pagination,
        "current_board": board_id,
        "current_sort": sort,
        "comment_count": comment_count,
        "banners": BannerModel.query.order_by(BannerModel.priority.desc()).all(),
    }
    return render_template("front/front_index.html", **context)


@ft_bp.get('/post/query/')
def query_post():
    post_kw = request.args.get("kw")
    print(post_kw)
    board_id = request.args.get("bd", type=int, default=-1)
    sort = request.args.get("st", default=1, type=int)
    page = request.args.get(get_page_parameter(), type=int, default=1)
    start = (page - 1) * config.PAGE_SIZE
    end = start + config.PAGE_SIZE
    post_query = None
    total = 0
    # 使用LEFT JOIN确保所有帖子都被包含
    comment_count_result = db.session.query(PostModel.id.label('post_id'),
                                            func.coalesce(func.count(CommentModel.id), 0).label('comment_num')
                                            ).outerjoin(
        CommentModel,
        PostModel.id == CommentModel.post_id
    ).group_by(PostModel.id).all()

    comment_count = {item.post_id: item.comment_num for item in comment_count_result}

    post_query = PostModel.query.filter(PostModel.title.like(f"%{post_kw}%"))
    if board_id != -1:
        post_query = post_query.filter_by(board_id=board_id)
    # 按发布时间排序
    if sort == 1:
        post_query = post_query.order_by(PostModel.create_time.desc())
    # 按照点赞数排序
    elif sort == 2:
        post_query = post_query.order_by(PostModel.likes_num.desc(), PostModel.create_time.desc())
    # 按照评论数排序
    elif sort == 3:
        if board_id == -1:
            post_query = db.session.query(PostModel).outerjoin(CommentModel,
                                                               PostModel.id == CommentModel.post_id).filter(
                PostModel.title.like(f"%{post_kw}%")).group_by(PostModel.id).order_by(
                func.count(CommentModel.id).desc(),
                PostModel.create_time.desc())
        else:
            post_query = db.session.query(PostModel).outerjoin(CommentModel,
                                                               PostModel.id == CommentModel.post_id).filter(
                PostModel.title.like(f"%{post_kw}%"), PostModel.board_id == board_id).group_by(PostModel.id).order_by(
                func.count(CommentModel.id).desc(),
                PostModel.create_time.desc())
    # 其他情况按照发布时间排序
    else:
        post_query = post_query.order_by(PostModel.create_time.desc())

    posts = post_query.slice(start, end)
    total = post_query.count()

    pagination = Pagination(bs_version=3, page=page, total=total, per_page=config.PAGE_SIZE, outer_window=0,
                            inner_window=2)

    context = {
        "boards": BoardModel.query.order_by(BoardModel.priority.desc()).all(),
        "posts": posts,
        "pagination": pagination,
        "current_board": board_id,
        "current_sort": sort,
        "comment_count": comment_count,
        "keyword": post_kw,
    }
    return render_template("front/query_post.html", **context)


@ft_bp.route('/login/', methods=['GET', 'POST'])
def login():
    if request.method == "GET":
        return render_template("front/front_login.html")
    else:
        form = LoginForm(request.form)
        if form.validate():
            email = form.email.data
            password = form.password.data
            remember = form.remember.data
            user = UserModel.query.filter_by(email=email).first()
            if not user:
                return restful.params_error(message="邮箱账号错误！")
            if not user.check_password(password):
                return restful.params_error(message="密码错误！")
            if not user.is_active:
                return restful.unlogin_error(message="账户已被禁用！")
            session["user_id"] = user.id
            # 只有员工身份，才生成token
            access_token = ""
            refreshed_token = ""
            permissions = []
            if user.is_staff:
                # 生成双token：access_token和refresh_token
                access_token = create_access_token(identity=user.id)
                refreshed_token = create_refresh_token(identity=user.id)
                for attr in dir(Permission):
                    if not attr.startswith("_"):
                        permission = getattr(Permission, attr)
                        if user.has_permission(permission):
                            permissions.append(attr.lower())
            if remember == 1:
                session.permanent = True

            user_dicts = user.to_dict()
            user_dicts["permissions"] = permissions
            # 在使用了sqlalchemy-serializer对ORM模型进行序列化之后，会给模型添加一个to_dict方法，使用这个方法可以将模型对象转为字典
            return restful.ok(message="登录成功！",
                              data={"access_token": access_token, "refresh_token": refreshed_token, "user": user_dicts})
        else:
            return restful.params_error(message=form.messages[0])


@ft_bp.route('/register/', methods=['GET', 'POST'])
def register():
    if request.method == "GET":
        return render_template("front/front_regist.html")
    else:
        form = RegisterForm(request.form)
        if form.validate():
            email = form.email.data
            username = form.username.data
            password = form.password.data
            identicon = Identicon()
            filenames = identicon.generate(text=md5(email.encode("utf-8")).hexdigest())
            avatar = filenames[2]
            user = UserModel(email=email, username=username, password=password, avatar=avatar)
            db.session.add(user)
            db.session.commit()
            return restful.ok(message="注册成功")
        else:
            return restful.params_error(message=form.messages[0])


@ft_bp.route("/logout/")
@login_required
def logout():
    session.clear()
    return redirect(url_for("front.login"))


# 邮箱验证码
@ft_bp.get('/email/captcha/')
def email_captcha():
    email = request.args.get('email')
    if not email:
        return jsonify({"coed": 400, "msg": "请传入邮箱账号"})
    captcha = random.randint(100000, 999999)
    subject = "【汇智论坛】注册验证码"
    body = "验证码：%d" % captcha
    bbs_celery.send_mail.delay(subject, [email], body)
    cache.set(email, str(captcha))
    print(cache.get(email))
    return restful.ok(message="发送成功！")


@ft_bp.route('/graph_captcha/')
def graph_captcha():
    image, captcha = Captcha.gene_graph_captcha()
    key = md5((captcha + str(time.time())).encode('utf-8')).hexdigest()
    cache.set(key, captcha)
    out = BytesIO()
    image.save(out, 'png')
    out.seek(0)
    resp = make_response(out.read())
    resp.content_type = "image/png"
    resp.set_cookie("graph_captcha_key", key, max_age=60 * 5)
    return resp


@ft_bp.get("/settings/person_info/")
@login_required
def person_info():
    return render_template("front/person_info.html")


@ft_bp.route("/settings/set_count/", methods=["GET", "POST"])
@login_required
def set_count():
    if request.method == "GET":
        return render_template("front/set_count.html")
    else:
        form = SetCountForm(request.form)
        if form.validate():
            username = form.username.data
            signature = form.signature.data
            user = UserModel.query.get(g.user.id)
            if not user:
                return restful.params_error(message="该用户不存在！")
            user.username = username
            user.signature = signature
            db.session.commit()
            return restful.ok()
        else:
            return restful.params_error(message=form.messages[0])


@ft_bp.route("/settings/set_count/change_email/", methods=["GET", "POST"])
@login_required
def change_email():
    if request.method == "GET":
        return render_template("front/change_email.html")
    else:
        email = g.user.email
        if not email:
            return restful.server_error(message="请先绑定邮箱！")
        captcha = random.randint(100000, 999999)
        subject = "【汇智论坛】修改邮箱验证码"
        body = f"您正在修改邮箱账号，验证码为：{captcha}，切勿将验证码泄露于他人，本条验证码有效期5分钟。"
        bbs_celery.send_mail.delay(subject, [email], body)
        cache.set(email, str(captcha))
        return restful.ok()


@ft_bp.post("/settings/set_count/change_email/check_captcha/")
@login_required
def check_email_captcha():
    form = CheckEmailCaptchaForm(request.form)
    if form.validate():
        captcha = form.captcha.data
        email = g.user.email
        cache_captcha = cache.get(email)
        if not cache_captcha or captcha != cache_captcha:
            return restful.params_error(message="邮箱验证码错误！")
        return restful.ok()
    else:
        return restful.params_error(message=form.messages[0])


@ft_bp.route("/settings/set_count/change_email/new_email/", methods=["GET", "POST"])
@login_required
def change_new_email():
    if request.method == "GET":
        return render_template("front/change_new_email.html")
    else:
        email = request.form.get("email")
        if not email:
            return restful.params_error(message="请传入新邮箱！")
        captcha = random.randint(100000, 999999)
        subject = "【汇智论坛】新邮箱验证码"
        body = f"您正在修改邮箱，新邮箱验证码为：{captcha}，切勿将验证码泄露于他人，本条验证码有效期5分钟。"
        bbs_celery.send_mail.delay(subject, [email], body)
        cache.set(email, str(captcha))
        return restful.ok()


@ft_bp.post("/settings/set_count/change_email/verify_new_email/")
@login_required
def varify_new_email():
    form = VarifyNewEmailForm(request.form)
    if form.validate():
        email = form.email.data
        cache.delete(email)
        user = UserModel.query.filter_by(email=g.user.email).first()
        if not user:
            return restful.params_error(message="原邮箱账号错误！")
        user.email = email
        db.session.commit()
        return restful.ok()
    else:
        return restful.params_error(message=form.messages[0])


@ft_bp.get("/settings/post/manage/")
@login_required
def manage_post():
    page = request.args.get(get_page_parameter(), 1, type=int)
    page_size = 5
    start = (page - 1) * page_size
    end = start + page_size
    query_obj = PostModel.query.filter_by(author_id=g.user.id).order_by(PostModel.create_time.desc())
    page_count = query_obj.count()
    posts = query_obj.slice(start, end).all()
    pagination = Pagination(bs_version=3, page=page, per_page=page_size, total=page_count, outerwindow=0,
                            innerwindow=2)
    context = {
        "posts": posts,
        "pagination": pagination,
    }
    return render_template("front/manage_post.html", **context)


@ft_bp.post("/settings/post/delete/")
@login_required
def delete_post():
    post_id = request.form.get("post_id")
    if not post_id:
        return restful.params_error(message="请传入帖子ID！")
    post = PostModel.query.get(post_id)
    if not post:
        return restful.params_error(message="帖子不存在！")
    db.session.delete(post)
    db.session.commit()
    return restful.ok()


@ft_bp.route("/settings/post/edit/", methods=["GET", "POST"])
def edit_post():
    if request.method == "GET":
        post_id = request.args.get("post_id")
        if not post_id:
            return restful.params_error(message="请传入帖子ID！")
        post = PostModel.query.get(post_id)
        if not post:
            return restful.params_error(message="帖子不存在！")
        boards = BoardModel.query.all()
        return render_template("front/edit_post.html", boards=boards, post=post)
    else:
        form = PostEditForm(request.form)
        if form.validate():
            title = form.title.data
            content = form.content.data
            board_id = form.board_id.data
            post_id = form.post_id.data
            board_model = BoardModel.query.get(board_id)
            if not board_model:
                return restful.params_error(message="板块不存在！")
            post = PostModel.query.get(post_id)
            if not post:
                return restful.params_error(message="帖子不存在！")
            try:
                post.title = title
                post.content = content
                post.board_id = board_id
                db.session.commit()
                return restful.ok()
            except DatabaseError as e:
                db.session.rollback()
                print(e)
                return restful.server_error(message="服务器内部错误！")
        else:
            return restful.params_error(message=form.messages[0])


@ft_bp.route("/settings/post/like/", methods=["GET", "POST"])
@login_required
def liked_posts():
    if request.method == "GET":
        posts = db.session.query(PostModel).join(LikeModel, PostModel.id == LikeModel.post_id).filter_by(
            user_id=g.user.id, like_status=1).order_by(PostModel.create_time.desc()).all()
        return render_template("front/liked_posts.html", posts=posts)
    else:
        post_id = request.form.get("post_id")
        if not post_id:
            return restful.params_error(message="请传入帖子ID！")
        post = PostModel.query.get(post_id)
        if not post:
            return restful.params_error(message="该篇帖子不存在！")
        like = LikeModel.query.filter_by(user_id=g.user.id, post_id=post_id).first()
        if not like:
            return restful.params_error(message="未查到该帖子点赞记录！")
        like.like_status = 0
        db.session.commit()
        return restful.ok()


@ft_bp.post("/avatar/upload/")
@login_required
def avatar_upload():
    form = UploadAvatarForm(request.files)
    if form.validate():
        avatar = form.avatar.data
        # 使用自己生成的文件名比较安全
        filename = avatar.filename
        _, ext = os.path.splitext(filename)
        filename = md5((g.user.email + str(time.time())).encode("utf-8")).hexdigest() + ext
        save_path = os.path.join(current_app.config["AVATARS_SAVE_PATH"], filename)
        avatar.save(save_path)
        g.user.avatar = filename
        db.session.commit()
        return restful.ok(message="上传成功！", data={"avatar": filename})
    else:
        message = form.messages[0]
        return restful.params_error(message=message)


@ft_bp.post("/profile/edit/")
@login_required
def profile_edit():
    form = SignatureForm(request.form)
    if form.validate():
        signature = form.signature.data
        g.user.signature = signature
        db.session.commit()
        return restful.ok()
    else:
        return restful.params_error(message=form.messages[0])


@ft_bp.route("/post/public/", methods=["GET", "POST"])
@login_required
def post_public():
    if request.method == "GET":
        boards = BoardModel.query.all()
        return render_template("front/public_post.html", boards=boards)
    else:
        form = PostPublicForm(request.form)
        if form.validate():
            title = form.title.data
            content = form.content.data
            board_id = form.board_id.data
            board = BoardModel.query.get(board_id)
            if not board:
                return restful.params_error(message="没有该板块！")
            post = PostModel(title=title, content=content, board_id=board_id, author=g.user)
            db.session.add(post)
            db.session.commit()
            return restful.ok(data={"id": post.id})
        else:
            return restful.params_error(message=form.messages[0])


@ft_bp.post("/post/image_upload/")
@login_required
def image_upload():
    form = UploadImageForm(request.files)
    if form.validate():
        image = form.image.data
        # 使用自己生成的文件名比较安全
        filename = image.filename
        _, ext = os.path.splitext(filename)
        filename = md5((g.user.email + str(time.time())).encode("utf-8")).hexdigest() + ext
        image_save_path = os.path.join(current_app.config["IMAGE_SAVE_PATH"], filename)
        image.save(image_save_path)
        return jsonify({
            "errno": 0,
            "data": {
                "url": url_for("media.get_post_image", filename=filename),
                "alt": filename,
                "href": ""
            }
        })
    else:
        message = form.messages[0]
        return jsonify({
            "errno": 1,
            "message": message,
        })


@ft_bp.get("/post/detail/<int:post_id>")
def post_detail(post_id):
    post = PostModel.query.get(post_id)
    comment_count = CommentModel.query.filter_by(post_id=post_id).count()
    if not post:
        return "404"
    context = {
        "post": post,
        "comment_count": comment_count,
    }
    if hasattr(g, "user"):
        context["like_status"] = db.session.query(LikeModel.like_status).filter_by(post_id=post_id,
                                                                                   user_id=g.user.id).first()
    return render_template("front/post_detail.html", **context)


@ft_bp.post("/comment/")
@login_required
def comment():
    form = CommentForm(request.form)
    if form.validate():
        content = form.content.data
        post_id = form.post_id.data
        post = PostModel.query.get(post_id)
        if not post:
            return restful.params_error("帖子不存在")
        comment_model = CommentModel(content=content, post_id=post_id, author=g.user)
        db.session.add(comment_model)
        db.session.commit()
        return restful.ok()
    else:
        message = form.messages[0]
        return restful.params_error(message=message)


@ft_bp.post("/post/like/")
@login_required
def like_post():
    form = LikeForm(request.form)
    if form.validate():
        post_id = form.post_id.data
        user_id = form.user_id.data
        like_status = form.like_status.data
        post = PostModel.query.get(post_id)
        if not post:
            return restful.params_error(message="帖子不存在")
        user = UserModel.query.get(user_id)
        if not user:
            return restful.params_error(message="用户不存在")
        if like_status == 1:
            post.likes_num += 1
        else:
            post.likes_num -= 1
        like_record = LikeModel.query.filter_by(post_id=post_id, user_id=user_id).first()
        if like_record and like_record.like_status != like_status:
            like_record.like_status = like_status
            db.session.commit()
            return restful.ok()
        else:
            like = LikeModel(post_id=post_id, user_id=user_id, like_status=like_status)
            db.session.add(like)
            db.session.commit()
            return restful.ok()
    else:
        message = form.messages[0]
        return restful.params_error(message=message)


@ft_bp.route("/cms/")
def cms_view():
    return render_template("cms/index.html")
